package eg.backend.domain;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.datanucleus.jpa.annotations.Extension;

import eg.backend.base.PermissionType;

@Entity
@NamedQueries( {
		//root dimensions created by given user 
		@NamedQuery(name = "Dimension.findUserRootDimensions", query = "select d from eg.backend.domain.Dimension d where d.owner = :user and d.parent = null"),
		//root dimension created by SYSTEM - predefined
		@NamedQuery(name = "Dimension.findSystemRootDimensions", query = "select d from eg.backend.domain.Dimension d where d.parent = null and d.ownerName = 'SYSTEM@kaixinji.com' "),
		//available child dimensions of parent to a given user 
		@NamedQuery(name = "Dimension.findUserAvailByParent", query = "select d from eg.backend.domain.Dimension d where d.parent = :parent and d.owner = :user"),
		@NamedQuery(name = "Dimension.findSystemAvailByParent", query = "select d from eg.backend.domain.Dimension d where d.parent = :parent and d.ownerName  = 'SYSTEM@kaixinji.com' "),

		@NamedQuery(name="Dimension.findSystemCategory", query="select d.encodedKey from eg.backend.domain.Dimension d where d.type='CATEGORY' and d.ownerName='SYSTEM' and d.position.startsWith(:position)"),
		@NamedQuery(name="Dimension.findUserCategory", query="select d.encodedKey from eg.backend.domain.Dimension d where d.type='CATEGORY' and d.owner = :user and d.position.startsWith(:position)"),
		
		@NamedQuery(name = "Dimension.findSharedDimensions", query = "select d from eg.backend.domain.Dimension d where d.owner = :user and d.permission in (:permissions)"
				+ " and (d.parent is null or not exists (select id from eg.backend.domain.Dimension t where t.id=d.parent and t.owner=:user and t.permission in (:permissions) ))"),
		@NamedQuery(name = "Dimension.findSharedDimensionsByParent", query = "select d from eg.backend.domain.Dimension d where d.owner = :user and d.permission in (:permissions) and d.parent = :parent"),

		@NamedQuery(name = "Dimension.findDescendantsOfUser", query = "select d from eg.backend.domain.Dimension d where d.rootDimension = :rootDimension and d.owner = :user and d.position like :position || '%'"),
		@NamedQuery(name = "Dimension.findDescendantsOfSystem", query = "select d from eg.backend.domain.Dimension d where d.rootDimension = :rootDimension and d.ownerName  = 'SYSTEM@kaixinji.com' and d.position like :position || '%'"),
		
		@NamedQuery(name = "Dimension.findDescendantIdsOfUser", query = "select d.encodedKey from eg.backend.domain.Dimension d where d.rootDimension = :rootDimension and d.owner = :user and d.position.startsWith(:position)"),
		@NamedQuery(name = "Dimension.findDescendantIdsOfSystem", query = "select d.encodedKey from eg.backend.domain.Dimension d where d.rootDimension = :rootDimension and d.ownerName  = 'SYSTEM@kaixinji.com' and d.position.startsWith(:position)"),
		@NamedQuery(name = "Dimension.deleteRootDimension", query = "delete from eg.backend.domain.Dimension d where d.rootDimension =:rootDimension"),
		@NamedQuery(name = "Dimension.delete", query = "delete from eg.backend.domain.Dimension d where d.encodedKey =:id"),
		@NamedQuery(name = "Dimension.getByKey", query = "select t from eg.backend.domain.Dimension t where t.encodedKey = :encodedKey ")
		})
public class Dimension {// user SYSTEM has predefined mandatory dimensions,
						// while user SYSTEM_SUGGEST contains predefined
						// optional dimensions - shared. His dimensions include
						// paymentType, region etc
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
	private String encodedKey;
	
	private String name;
	private String description;
	
	private String parent;

	private String owner;
	private String ownerName;//to determine SYSTEM and SYSTEM_SHARED
	@Enumerated(EnumType.STRING)
	private PermissionType permission;

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private List<Dimension> children = new ArrayList<Dimension>();

	@Transient
	private List<Dimension> tempChildren = new ArrayList<Dimension>();
	
	private String rootDimension;

	@Column(name = "self_position")
	private String position;// for root, position="0." rather than "0"

	private int childPosition;
	
	private Set<String> records = new HashSet<String>();
	
	private String type;//CATEGORY TIME COMMON - user can change time category to remove QUARTER or add WEEK
	
	public String getEncodedKey() {
		return encodedKey;
	}

	public void setEncodedKey(String encodedKey) {
		this.encodedKey = encodedKey;
	}

	public void setParent(String parent) {
		this.parent = parent;
	}

	public void setRootDimension(String rootDimension) {
		this.rootDimension = rootDimension;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getOwner() {
		return owner;
	}

	public void setOwner(String owner) {
		this.owner = owner;
	}

	public PermissionType getPermission() {
		return permission;
	}

	public void setPermission(PermissionType permission) {
		this.permission = permission;
	}

	public List<Dimension> getChildren() {
		return children;
	}

	public void setChildren(List<Dimension> children) {
		this.children = children;
	}

	public String getPosition() {
		return position;
	}

	public void setPosition(String position) {
		this.position = position;
	}

	public int getChildPosition() {
		return childPosition;
	}

	public void setChildPosition(int childPosition) {
		this.childPosition = childPosition;
	}

	public String getParent() {
		return parent;
	}

	public String getRootDimension() {
		return rootDimension;
	}

	public Set<String> getRecords() {
		return records;
	}

	public void setRecords(Set<String> records) {
		this.records = records;
	}

	public String getOwnerName() {
		return ownerName;
	}

	public void setOwnerName(String ownerName) {
		this.ownerName = ownerName;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getId() {
		return this.encodedKey;
	}

	public List<Dimension> getTempChildren() {
		return tempChildren;
	}

	public void setTempChildren(List<Dimension> tempChildren) {
		this.tempChildren = tempChildren;
	}

	/* common */
	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.getEncodedKey())
				.append(this.getName()).toHashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!this.getClass().isInstance(obj)) {
			return false;
		}

		Dimension dimension = (Dimension) obj;
		return new EqualsBuilder().append(this.getEncodedKey(), dimension.getEncodedKey()).isEquals();
	}
}
